#pragma once


//////////////////////////////////////////////////////////////////////////
template <typename Param>
__inline Param __DataFromVar(_variant_t& var)
{
    if(var.vt == VT_EMPTY)
        return (Param());
    return (Param)var;
}

template <>
__inline BSTR __DataFromVar(_variant_t& var)
{
    if(var.vt == VT_EMPTY)
        return NULL;
    return var.bstrVal;
}

template <typename Param>
__inline void __VarFromData(Param param, VARIANT* pVar)
{
    if(pVar != NULL)
    {
        _variant_t varTemp(param);
        memcpy(pVar, &(VARIANT)varTemp, sizeof(VARIANT));
    }
}

template <>
__inline void __VarFromData(BSTR param, VARIANT* pVar)
{
    if(pVar != NULL)
    {
        pVar->vt = VT_BSTR;
        pVar->bstrVal = param;
    }
}

//////////////////////////////////////////////////////////////////////////
class DispMemInvoker
{
public:
    virtual HRESULT Invoke(void* pThis, VARIANT* pVarResult, DISPPARAMS* pParam)
    {
        return E_NOTIMPL;
    }
};

struct stDispMemInfo
{
    LPCTSTR         szName;
    DispMemInvoker* invoker;
};

struct stDispMemInfoMap
{
    stDispMemInfoMap*   pBaseMap;
    stDispMemInfo*      pDispMemInfo;
};

//////////////////////////////////////////////////////////////////////////

template < typename T, typename Ret>
class DispMemInvokerImpl0 : public DispMemInvoker
{
protected:
    typedef Ret (T::*FuncDef)();
    FuncDef m_Func;
public:

    DispMemInvokerImpl0(FuncDef Func)
    {
        m_Func = Func;
    }

    virtual HRESULT Invoke(void* p, VARIANT* pVarResult, DISPPARAMS* pParam)
    {
        if(pParam->cArgs != 0)
            return DISP_E_BADPARAMCOUNT;

        T* pThis = (T*)p;

        Ret ret = (pThis->*m_Func)();
        __VarFromData<Ret>(ret, pVarResult);
        return S_OK;
    }
};

template < typename T, typename Ret, typename Param1>
class DispMemInvokerImpl1 : public DispMemInvoker
{
protected:
    typedef Ret (T::*FuncDef)(Param1);
    FuncDef m_Func;
public:

    DispMemInvokerImpl1(FuncDef Func)
    {
        m_Func = Func;
    }

    virtual HRESULT Invoke(void* p, VARIANT* pVarResult, DISPPARAMS* pParam)
    {
        if(pParam->cArgs != 1)
            return DISP_E_BADPARAMCOUNT;

        _variant_t varParam1(pParam->rgvarg[0]);

        T* pThis = (T*)p;

        Ret ret = (pThis->*m_Func)(__DataFromVar<Param1>(varParam1));
        __VarFromData<Ret>(ret, pVarResult);
        return S_OK;
    }
};

template < typename T, typename Ret, typename Param1, typename Param2 >
class DispMemInvokerImpl2 : public DispMemInvoker
{
protected:
    typedef Ret (T::*FuncDef)(Param1, Param2);
    FuncDef m_Func;
public:

    DispMemInvokerImpl2(FuncDef Func)
    {
        m_Func = Func;
    }

    virtual HRESULT Invoke(void* p, VARIANT* pVarResult, DISPPARAMS* pParam)
    {
        if(pParam->cArgs != 2)
            return DISP_E_BADPARAMCOUNT;

        _variant_t varParam1(pParam->rgvarg[1]);
        _variant_t varParam2(pParam->rgvarg[0]);

        T* pThis = (T*)p;

        Ret ret = (pThis->*m_Func)(__DataFromVar<Param1>(varParam1), __DataFromVar<Param2>(varParam2));
        __VarFromData<Ret>(ret, pVarResult);

        return S_OK;
    }
};

template < typename T, typename Ret, typename Param1, typename Param2, typename Param3 >
class DispMemInvokerImpl3 : public DispMemInvoker
{
protected:
    typedef Ret (T::*FuncDef)(Param1, Param2, Param3);
    FuncDef m_Func;
public:

    DispMemInvokerImpl3(FuncDef Func)
    {
        m_Func = Func;
    }

    virtual HRESULT Invoke(void* p, VARIANT* pVarResult, DISPPARAMS* pParam)
    {
        if(pParam->cArgs != 3)
            return DISP_E_BADPARAMCOUNT;

        _variant_t varParam1(pParam->rgvarg[2]);
        _variant_t varParam2(pParam->rgvarg[1]);
        _variant_t varParam3(pParam->rgvarg[0]);

        T* pThis = (T*)p;

        Ret ret = (pThis->*m_Func)(__DataFromVar<Param1>(varParam1), __DataFromVar<Param2>(varParam2), __DataFromVar<Param3>(varParam3));
        __VarFromData<Ret>(ret, pVarResult);

        return S_OK;
    }
};

template < typename T, typename Ret, typename Param1, typename Param2, typename Param3, typename Param4 >
class DispMemInvokerImpl4 : public DispMemInvoker
{
protected:
    typedef Ret (T::*FuncDef)(Param1, Param2, Param3, Param4);
    FuncDef m_Func;
public:

    DispMemInvokerImpl4(FuncDef Func)
    {
        m_Func = Func;
    }

    virtual HRESULT Invoke(void* p, VARIANT* pVarResult, DISPPARAMS* pParam)
    {
        if(pParam->cArgs != 4)
            return DISP_E_BADPARAMCOUNT;

        _variant_t varParam1(pParam->rgvarg[3]);
        _variant_t varParam2(pParam->rgvarg[2]);
        _variant_t varParam3(pParam->rgvarg[1]);
        _variant_t varParam4(pParam->rgvarg[0]);

        T* pThis = (T*)p;

        Ret ret = (pThis->*m_Func)(__DataFromVar<Param1>(varParam1), __DataFromVar<Param2>(varParam2), __DataFromVar<Param3>(varParam3), __DataFromVar<Param4>(varParam4));
        __VarFromData<Ret>(ret, pVarResult);

        return S_OK;
    }
};

template < typename T, typename Ret, typename Param1, typename Param2, typename Param3, typename Param4, typename Param5 >
class DispMemInvokerImpl5 : public DispMemInvoker
{
protected:
    typedef Ret (T::*FuncDef)(Param1, Param2, Param3, Param4, Param5);
    FuncDef m_Func;
public:

    DispMemInvokerImpl5(FuncDef Func)
    {
        m_Func = Func;
    }

    virtual HRESULT Invoke(void* p, VARIANT* pVarResult, DISPPARAMS* pParam)
    {
        if(pParam->cArgs != 5)
            return DISP_E_BADPARAMCOUNT;

        _variant_t varParam1(pParam->rgvarg[4]);
        _variant_t varParam2(pParam->rgvarg[3]);
        _variant_t varParam3(pParam->rgvarg[2]);
        _variant_t varParam4(pParam->rgvarg[1]);
        _variant_t varParam5(pParam->rgvarg[0]);

        T* pThis = (T*)p;

        Ret ret = (pThis->*m_Func)(__DataFromVar<Param1>(varParam1),
            __DataFromVar<Param2>(varParam2),
            __DataFromVar<Param3>(varParam3),
            __DataFromVar<Param4>(varParam4),
            __DataFromVar<Param5>(varParam5));

        __VarFromData<Ret>(ret, pVarResult);

        return S_OK;
    }
};



#define XDEF_DISPATCH_MAP(theClass, baseClass)      \
    stDispMemInfo* GetDispMemInfo();                \
    stDispMemInfoMap* GetBaseDispMemInfoMap()       \
    {                                               \
        return baseClass::GetDispMemInfoMap();      \
    }                                               \
    virtual stDispMemInfoMap* GetDispMemInfoMap();


#define XIMP_DISPATCH_MAP(theClass, baseClass)      \
    stDispMemInfoMap* theClass::GetDispMemInfoMap() \
    {                                               \
        static stDispMemInfoMap infoMap =           \
        {                                           \
            GetBaseDispMemInfoMap(),                \
            GetDispMemInfo()                        \
        };                                          \
        return &infoMap;                            \
    }                                               \
    stDispMemInfo* theClass::GetDispMemInfo()       \
    {                                               \
        static stDispMemInfo info[] =               \
        {

#define XEND_DISPATCH_MAP()                         \
            {NULL, NULL},                           \
        };                                          \
        return info;                                \
}


#define XDEF_DISP_METHOD0(name, class, returnType, func) \
    {name, new DispMemInvokerImpl0<class, returnType>(func) },

#define XDEF_DISP_METHOD1(name, class, returnType, param1Type, func) \
    {name, new DispMemInvokerImpl1<class, returnType, param1Type>(func) },

#define XDEF_DISP_METHOD2(name, class, returnType, param1Type, paramType2, func) \
    {name, new DispMemInvokerImpl2<class, returnType, param1Type, paramType2>(func) },

#define XDEF_DISP_METHOD3(name, class, returnType, param1Type, paramType2, paramType3, func) \
    {name, new DispMemInvokerImpl3<class, returnType, param1Type, paramType2, paramType3>(func) },

#define XDEF_DISP_METHOD4(name, class, returnType, param1Type, paramType2, paramType3, paramType4, func) \
    {name, new DispMemInvokerImpl4<class, returnType, param1Type, paramType2, paramType3, paramType4>(func) },

#define XDEF_DISP_METHOD5(name, class, returnType, param1Type, paramType2, paramType3, paramType4, paramType5, func) \
    {name, new DispMemInvokerImpl5<class, returnType, param1Type, paramType2, paramType3, paramType4, paramType5>(func) },

